package com.harboursoftware.xstorage.web;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.mortbay.log.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.harboursoftware.xstorage.XStorageException;
import com.harboursoftware.xstorage.operations.PutBucket;
import com.harboursoftware.xstorage.operations.PutObject;
import com.harboursoftware.xstorage.util.MultimapUtils;

/**
 * PostObject异于其他的请求，所以把对post请求的解析独立分离出来.
 * 
 * @author Simon Leung
 * @since 0.1
 */
class PostObjectRequestParser {
    private final static Logger log = LoggerFactory.getLogger(PostObjectRequestParser.class);
    private static ThreadLocal<Map<String, Object>> postRequest = new ThreadLocal<Map<String, Object>>();
    
    private final static String KEY_PARAMETERS = "parameters";
    private final static String KEY_HEADERS = "headers";
    private final static String KEY_INPUT_STREAM = "inputStream";

    @SuppressWarnings("unchecked")
    public static Map<String, String> parsePostRequestParameters(HttpServletRequest request) throws XStorageException,
        IOException {
        Map<String, Object> postRequestMap = parsePostRequest(request);
        return (Map<String, String>) postRequestMap.get(KEY_PARAMETERS);
    }

    @SuppressWarnings("unchecked")
    public static ListMultimap<String, String> parsePostRequestHeaders(HttpServletRequest request)
        throws XStorageException, IOException {
        Map<String, Object> postRequestMap = parsePostRequest(request);
        return (ListMultimap<String, String>) postRequestMap.get(KEY_HEADERS);
    }

    public static InputStream parsePostRequestInputStream(HttpServletRequest request) throws XStorageException,
        IOException {
        Map<String, Object> postRequestMap = parsePostRequest(request);
        return (InputStream) postRequestMap.get(KEY_INPUT_STREAM);
    }

    /**
     * 解析和校验content type="multipart/form-data"的Post HTTP请求参数，把请求的form field转化成与{@link PutBucket}兼容的headers、parameters,
     * 解析后把结果存储在线程变量中
     * <p>
     * 参考:<a href="http://docs.amazonwebservices.com/AmazonS3/latest/dev/UsingHTTPPOST.html">Amazon S3 Browser-Based
     * Uploads Using POST</a>
     * </p>
     * 
     * @param request
     * @throws XStorageException
     * @throws IOException
     * @see #parsePostRequestParameters
     * @see #parsePostRequestHeaders
     * @see #parsePostRequestInputStream
     */
    private static Map<String, Object> parsePostRequest(HttpServletRequest request) throws XStorageException,
        IOException {
        Map<String, Object> postRequestMap = postRequest.get();
        if (postRequestMap != null) {
            return postRequestMap;
        }

        postRequestMap = new HashMap<String, Object>();
        postRequest.set(postRequestMap);

        ListMultimap<String, String> postFormFields = parsePostFormFields(request);
        validatePostFormFields(postFormFields);
        splitPostFormFields(postFormFields);

        return postRequestMap;
    }

    /**
     * 把Post请求的form fields转化为map的形式
     * 
     * @throws XStorageException
     * @throws IOException
     */
    private static ListMultimap<String, String> parsePostFormFields(HttpServletRequest request)
        throws XStorageException, IOException {
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            // TODO：需要测试S3是如何响应到客户端的,需要把ErrroResult带到外层
            throw new XStorageException("the post request is not multipart");
        }
        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload();
        ListMultimap<String, String> postFormFieldMap = ArrayListMultimap.create();
        try {
            // Parse the request
            FileItemIterator itemIterator = upload.getItemIterator(request);
            while (itemIterator.hasNext()) {
                FileItemStream item = itemIterator.next();
                String name = item.getFieldName();
                InputStream stream = item.openStream();
                if (name.equals("file")) {
                    postRequest.get().put("inputStream", stream);
                    // The file must be the last field in the form.
                    break;
                } else if (item.isFormField()) {
                    String value = IOUtils.toString(stream, "UTF-8");
                    postFormFieldMap.put(name, value);
                    log.debug("parse POST form field, {} : {}", name, value);
                }
            }

            if (postRequest.get().get("inputStream") == null) {
                // TODO:异常处理
            }
        } catch (FileUploadException e) {
            throw new XStorageException("error occurred when handling post request parameters", e);
        }
        return postFormFieldMap;
    }

    /**
     * 校验Post请求是否符合policy所指定的约束
     * 
     * @throws XStorageException
     */
    private static void validatePostFormFields(ListMultimap<String, String> postFormFields) throws XStorageException {
        // 检查是否包括所有的必须的属性
        boolean containsKey = postFormFields.containsKey("key");
        boolean containsPolicy = postFormFields.containsKey("policy");
        boolean containsAllRequiredParamters = containsKey && containsPolicy;
        if (!containsAllRequiredParamters) {
            // TODO：需要测试S3是如何响应到客户端的,需要把ErrroResult带到外层
            throw new XStorageException("the post request does not contain all the required parameters");
        }

        String policyString = postFormFields.get("policy").get(0);
        PostPolicy policy = PostPolicy.fromBase64Json(policyString);

        boolean matchs = policy.matchs(postFormFields);
        if (!matchs) {
            // TODO：需要测试S3是如何响应到客户端的,需要把ErrroResult带到外层
            throw new XStorageException();
        }
    }

    /**
     * 将Post form fields拆分成适配{@link PutObject}的模式的parameters 和 headers。
     * 
     * @see #parsePostRequestParameters
     * @see #parsePostRequestHeaders
     * @see #parsePostRequestInputStream
     */
    private static void splitPostFormFields(ListMultimap<String, String> postFormFields) {
        Map<String, String> parameters = new HashMap<String, String>();
        
        for(String key : postFormFields.keySet()) {
            if(!isHeader(key)) {
                String value = MultimapUtils.getFirstValue(postFormFields, key);
                parameters.put(key, value);
            }
        }
        
        for(String key : parameters.keySet()) {
            postFormFields.removeAll(key);
        }
        
        Map<String, Object> postRequestMap = postRequest.get();
        postRequestMap.put(KEY_PARAMETERS, parameters);
        postRequestMap.put(KEY_HEADERS, postFormFields);
    }

    private static boolean isHeader(String key) {
        return "Cache-Control".equals(key) || "Content-Type".equals(key) || "Content-Disposition".equals(key)
            || " Content-Encoding".equals(key) || "Expires".equals(key) || key.startsWith("x-amz-meta-");
    }
}
